package cn.liuxiany.alibaba.consumer.service.impl;

import cn.liuxiany.alibaba.consumer.entity.Order;
import cn.liuxiany.alibaba.consumer.mapper.OrderMapper;
import cn.liuxiany.alibaba.consumer.service.IOrderService;
import cn.liuxiany.alibaba.consumer.util.CircuitBreakerUtil;
import cn.liuxiany.alibaba.provider.api.HelloService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.shardingsphere.transaction.core.TransactionType;
import org.apache.shardingsphere.transaction.core.TransactionTypeHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.List;

/**
 * @author liuxiany
 * @date 2020/07/10
 */
@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private CircuitBreakerRegistry circuitBreakerRegistry;

    /**
     * cluster: 集群容错模式
     * 文档https://cn.dubbo.apache.org/zh-cn/docsv2.7/user/examples/fault-tolerent-strategy/#%E9%9B%86%E7%BE%A4%E6%A8%A1%E5%BC%8F%E9%85%8D%E7%BD%AE
     * 默认为failover。失败自动切换，当出现失败，重试其它服务器。通常用于读操作，但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。
     * failfast 。快速失败，只发起一次调用，失败立即报错。通常用于非幂等性的写操作，比如新增记录。
     * failback.失败自动恢复，后台记录失败请求，定时重发。通常用于消息通知操作
     * 如果provider没启动，也会进入mock的降级
     */
//    @DubboReference(loadbalance = "roundrobin", timeout = 5000, check = false, mock = "cn.liuxiany.alibaba.provider.mock.HelloServiceMock")
    @DubboReference(loadbalance = "roundrobin", cluster = "failfast", timeout = 15000, mock = "cn.liuxiany.alibaba.provider.mock.HelloServiceMock")
    private HelloService helloService;

    @Override
    public void testAdd(Order order) {

        log.info("order is {}", order.toString());

        orderMapper.insert(order);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void testAddTransaction(Order order) {

        log.info("order is {}", order.toString());

        orderMapper.insert(order);

        String s = null;

        s.substring(0);
    }

    //    @TimeLimiter(name = "backendA")
    @Override
    @CircuitBreaker(name = "backendA", fallbackMethod = "testFallback")
    public String testBreak(String name) {
        String result = null;
//        try {

        CircuitBreakerUtil.getCircuitBreakerStatus("执行开始前：", circuitBreakerRegistry.circuitBreaker("backendA"));
        result = helloService.hello(name);
        log.info("this is result :{}", result);
//        } catch (java.lang.RuntimeException e) {
        // catch调异常会使熔断失效
//            result = "捕获了异常";
//        }

        CircuitBreakerUtil.getCircuitBreakerStatus("执行结束后：", circuitBreakerRegistry.circuitBreaker("backendA"));
        return result;
    }

    @Override
    @Transactional
    public List<Order> testGet(String name) {
        // 构建查询条件
        LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<>();
        if (!StringUtils.isEmpty(name)) {
            queryWrapper.like(Order::getName, name);
        }

        return orderMapper.selectList(queryWrapper);
    }

    /**
     * 测试sharding-jdbc 分布式事务
     * 必须选择TransactionType的模式，XA为强一致性事务，BASE为柔性事务
     *
     * @param order
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @GlobalTransactional(rollbackFor = Exception.class)
//    @ShardingTransactionType(TransactionType.BASE)
    public String testGlobalTx(Order order) {
        TransactionTypeHolder.set(TransactionType.BASE);
        log.info("order is {}", order.toString());
//        orderMapper.insert(order);

        // 强制走主库
//        HintManager instance = HintManager.getInstance();
//        instance.setMasterRouteOnly();

        // 测试下同时读写，是否hai

        LambdaUpdateWrapper<Order> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(Order::getName, "sharding-gobal-tx-update-ex");
        updateWrapper.eq(Order::getId, "1727934049273524226");

        orderMapper.update(updateWrapper);

        // 调用其他服务
        String s1 = helloService.addUserScore(order.getId());

        // 模拟consumer端异常
        String s = null;
        s.substring(0);

        return s1;
    }

    @Override
    @CircuitBreaker(name = "backendA", fallbackMethod = "testFallback")
    public String testBreak2(String name) {
        String result = null;

        CircuitBreakerUtil.getCircuitBreakerStatus("执行开始前：", circuitBreakerRegistry.circuitBreaker("backendA"));
        result = helloService.hello(name);
        log.info("this is result :{}", result);

        CircuitBreakerUtil.getCircuitBreakerStatus("执行结束后：", circuitBreakerRegistry.circuitBreaker("backendA"));
        return result;
    }

    /*public String testFallback(java.lang.Throwable throwable, String name) {

        return "带参数的fallback" + name;
    }*/

    public String testFallback(String name, java.lang.Throwable throwable) {

        if (throwable instanceof io.github.resilience4j.circuitbreaker.CallNotPermittedException) {
            log.info("CallNotPermittedException啊啊啊啊啊");
        } else {
            log.info("其他exception啊啊啊啊");
        }

        log.info("exception is {}", throwable.getClass());

        CircuitBreakerUtil.getCircuitBreakerStatus("降级方法中：", circuitBreakerRegistry.circuitBreaker("backendA"));

        return "不带参数的fallback";
    }

    public String testFallback(io.github.resilience4j.circuitbreaker.CallNotPermittedException callNotPermittedException) {

        log.info("熔断器已经打开，拒绝访问被保护方法~");
        CircuitBreakerUtil.getCircuitBreakerStatus("熔断器打开中:", circuitBreakerRegistry.circuitBreaker("backendA"));

        return "CallNotPermittedException拉拉拉";
    }
}
